// Copyright (C) 2011 by Christopher Diggins
// Usage is permitted under the terms of the MIT License 1.0. 	

/** \file 
	Contains implementation of memory management functions.
	\see \ref mem
*/

/** Returns a non-zero value if the buffer is full. */
int buffer_full(Buffer* b)
{
	return b->cur == b->end;
}

/** Adds a new buffer to the memory manager object. */
void add_new_buffer(Mem* m)
{
	Buffer* r = (Buffer*)malloc(sizeof(Buffer));	
	r->data = (Val*)malloc(HEAP_BUFFER_COUNT * sizeof(Val*));
	r->cur = r->data;
	r->end = r->data + HEAP_BUFFER_COUNT;
	r->prev = m->buffer;
	r->next = NULL;
	m->buffer = r;
}

/** Calls a visitor function on a Val* and every object
	it references in depth first order. A Visited flag is set in order to 
	assure that every Val is only visited once. */
void mem_val_visit_with(Val* v, VisitFxn f, void* a) 
{
	if (val_has_flag(v, VisitedFlag)) 
		return;

	val_set_flag(v, VisitedFlag);
	
	if (val_has_array(v))
	{
		Val** begin = obj_begin(v);
		Val** end = obj_end(v);
		while (begin != end) 
			mem_val_visit_with(*begin++, f, a);
	}

	if (f) f(v, a);
}

void mem_init(Mem* m)
{
	m->buffer = NULL;
	m->deleted = NULL;
	add_new_buffer(m);
}

void mem_free(Mem* m)
{
	Buffer* b = m->buffer; 
	Buffer* tmp = NULL;
	m->deleted = NULL;
	while (b != NULL) {
		tmp = (Buffer*)b->prev;
		free(b);
		b = tmp;
	}
	m->buffer = NULL;
}

Buffer* mem_first_buffer(Mem* m)
{
	Buffer* r = m->buffer;
	while (r && r->prev) r = (Buffer*)r->prev;
	return r;
}

/** Returns a memory iterator pointing to the first memory location. */
MemIter mem_first(Mem* m)
{
	MemIter r;
	r.buffer = mem_first_buffer(m);
	r.mem = m;
	r.val = r.buffer->data - 1;
	return r;
}

/** Increments a memory iterator so that it points to the next memory location. */
uint mem_next(MemIter* iter)
{	
	if (!iter->buffer) 
		return 0;
	while (1) 
	{
		if (++iter->val < iter->buffer->cur) 
			return 1;
		iter->buffer = (Buffer*)iter->buffer->next;
		if (!iter->buffer) 
			return 0;
		iter->val = iter->buffer->data - 1;
	}
}

Val* mem_iter_val(MemIter* iter)
{
	return iter->val;
}
	
Val* mem_val_alloc(Mem* m) 
{
	Val* r;
	if (m->deleted != NULL)
	{
		r = m->deleted;
		m->deleted = (Val*)r->data.PointerData.val;
	}
	else 
	{
		if (buffer_full(m->buffer))
			add_new_buffer(m);
		r = m->buffer->cur++;
	}
	memset(r, 0, sizeof(Val));
	r->refs = 1;
	return r;
}

void mem_val_free(Mem* m, Val* v)
{	
	v->data.PointerData.val = m->deleted;
	m->deleted = v;
}
